package ru.susu.algebra.grouprings;

import java.io.BufferedOutputStream;
import java.io.FileNotFoundException;
import java.io.FileOutputStream;
import java.io.PrintStream;
import java.math.BigInteger;
import java.util.ArrayList;
import java.util.Date;
import java.util.List;

import ru.susu.algebra.chartable.constructor.AlternatingCharTableUtil;
import ru.susu.algebra.chartable.constructor.AlternatingGroupCharTableConstructor;
import ru.susu.algebra.field.quadratic.QuadraticFieldGeneralForm;
import ru.susu.algebra.field.quadratic.QuadraticInteger;
import ru.susu.algebra.matrix.Matrix;
import ru.susu.algebra.partition.Partition;
import ru.susu.algebra.partition.PartitionsCacheConstructor;
import ru.susu.algebra.partition.comparators.AlternatingByCharDegreeComparator;
import ru.susu.algebra.partition.comparators.Partition4SpecialColumnLast;
import ru.susu.algebra.partition.filter.AlternatingPartitionsFilter;
import ru.susu.algebra.partition.filter.OnlyFirstAssocPartitionFilter;
import ru.susu.algebra.ui.field.SimpleLegendField;
import ru.susu.algebra.util.CollectionUtils;
import ru.susu.algebra.util.NumberUtilities;
import ru.susu.algebra.wrapper.number.BigIntegerFraction;

/**
 * @author akargapolov
 * @since: 14.06.2010
 */
public class A14
{
	private static final BigInteger TWO = BigInteger.valueOf(2);

	private static List<Partition> partitions1 = CollectionUtils.sort(new PartitionsCacheConstructor().getAscendingPartitions(14, new OnlyFirstAssocPartitionFilter()),
			true, new AlternatingByCharDegreeComparator());
	private static List<Partition> partitions2 = CollectionUtils.sort(new PartitionsCacheConstructor().getAscendingPartitions(14, new AlternatingPartitionsFilter()),
			true, new Partition4SpecialColumnLast());
	private static AlternatingGroupCharTableConstructor constructor = AlternatingGroupCharTableConstructor.getInstance();
	private static Integer[] rows = getRows();
	private static QuadraticFieldGeneralForm[] UNITS = {new QuadraticFieldGeneralForm(1, 1, 13), new QuadraticFieldGeneralForm(19, 8, 33), new QuadraticFieldGeneralForm(0, 1,5)};
	private static BigInteger[] POWS = {BigInteger.valueOf(3360), BigInteger.valueOf(840), BigInteger.valueOf(2016)};
	private static BigInteger factorial = NumberUtilities.getFactorial(14).divide(BigInteger.valueOf(2));
	//по возрастанию степени

	private static Integer[] e = {1, 1, 1};
	private static BigInteger[] pows = new BigInteger[3];


	public static void main(String[] args) throws Exception
	{
		//40 32 8 0
		int[] numbers = {0, 8, 32, 40};
		//for (int i : numbers)
		for (int i = 36;i < 37;i++)
		{
			Date start = new Date();
			System.out.print("Start processing " + i + ". ");
			e[0] = (i & 1) == 0 ? 1 : -1;
			e[1] = (i & 2) == 0 ? 1 : -1;
			e[2] = (i & 4) == 0 ? 1 : -1;
			pows[0] = (i & 8) == 0 ? BigInteger.ZERO : POWS[0];//.divide(BigInteger.valueOf(13));
			pows[1] = (i & 16) == 0 ? BigInteger.ZERO : POWS[1];//.divide(TWO);
			pows[2] = (i & 32) == 0 ? BigInteger.ZERO : POWS[2];//.divide(TWO);

			try
			{
				FileOutputStream stream = new FileOutputStream("ExecuteResults/A14/"
						+ pows[0] + "_" + pows[1] + "_" + pows[2] + "_" + e[0] + "_" + e[1] + "_" + e[2] + ".txt");
				BufferedOutputStream buffered = new BufferedOutputStream(stream);
				PrintStream printStream = new PrintStream(buffered);
				check(printStream);
				printStream.close();
			}
			catch(FileNotFoundException e)
			{
				throw new RuntimeException(e);
			}
			System.out.println("Processed in " + (new Date().getTime() - start.getTime()) + "ms");
		}
	}

	private static void check(PrintStream writer) throws Exception
	{
		writer.println("Start checking");
		writer.println("e[0] = " + e[0] + " e[1] = " + e[1] + " e[2] = " + e[2]);
		writer.println("k[0] = " + pows[0] + " k[1] = " + pows[1] + " k[2] = " + pows[2]);

		for (int j = partitions2.size() - 1; j >= 0; j--)
		{
			writer.println(j + " partition: " + partitions2.get(j) + " [" + partitions2.get(j).getOrderNumber() + "]");
			BigIntegerFraction fraction = getStaticPart(writer, j);
			writer.println("Static part: " + fraction);
			if (!AlternatingCharTableUtil.isSpecialColumn(partitions2.get(j)))
			{
				for (int i = 0; i < rows.length; i++)
				{
					BigIntegerFraction unitPart = getUnitPart(writer, i, j, false);
					writer.print(i + " unit part: " + unitPart + " = ");
					getUnitPart(writer, i, j, true);
					fraction = fraction.add(unitPart);
					writer.println();
				}
				writer.println("Sum: " + fraction);
				if (!fraction.getDenominator().equals(BigInteger.ONE))
				{
					break;
				}
			}
			else
			{
				BigIntegerFraction sum[] = {fraction, fraction};
				for (int i = 0; i < rows.length; i++)
				{
					for (int k = 0;k< 2;k++)
					{
						BigIntegerFraction tmp = getUnitColumnPart(writer, i, j, k, false);
						writer.println(tmp);
						sum[k] = sum[k].add(tmp);
					}
				}
				writer.println("Sum[0]: " + sum[0]);
				writer.println("Sum[1]: " + sum[1]);
				if (!sum[0].getDenominator().equals(BigInteger.ONE) || !sum[1].getDenominator().equals(BigInteger.ONE))
				{
					break;
				}
			}
		}
	}

	private static boolean isAllZero(Matrix matrix, int col)
	{
		for (int i = 0;i<matrix.getRows();i++)
			if (!(matrix.getValue(i, col) instanceof BigInteger) || !BigInteger.ZERO.equals(matrix.getValue(i, col)))
			{
				return false;
			}
		return true;
	}

	private static BigIntegerFraction getUnitColumnPart(PrintStream writer, int unit, int partition2, int col, boolean debug) throws Exception
	{
		Partition p1 = partitions1.get(rows[unit]);
		Partition p2 = partitions2.get(partition2);
		Matrix matrix = constructor.getValue(p1, p2);
		if (isAllZero(matrix, col))
		{
			return BigIntegerFraction.ZERO;
		}
		QuadraticFieldGeneralForm gunit = (QuadraticFieldGeneralForm)UNITS[unit].modPow(pows[unit], factorial);
		QuadraticInteger value1 =((SimpleLegendField<QuadraticInteger>)matrix.getValue(0, col)).getValue();
		QuadraticFieldGeneralForm firstPart = (QuadraticFieldGeneralForm)gunit.multiply(value1);
		QuadraticInteger value2 =((SimpleLegendField<QuadraticInteger>)matrix.getValue(1, col)).getValue();
		QuadraticFieldGeneralForm secondPart = (QuadraticFieldGeneralForm)getConjugatePow(UNITS[unit], pows[unit]).multiply(value2);
		QuadraticInteger summ = firstPart.add(secondPart);
		if (!summ.getB().equals(BigInteger.ZERO))
		{
			throw new RuntimeException("I can't process not integer");
		}
		BigInteger tmp = summ.getA().multiply(BigInteger.valueOf(e[unit]));
		tmp = tmp.multiply(AlternatingCharTableUtil.calcAlternatingDegree(p1));//mul deg
		return new BigIntegerFraction(tmp, factorial);
	}

	private static QuadraticFieldGeneralForm getConjugatePow(QuadraticFieldGeneralForm gunit, BigInteger pow) throws Exception
	{
		QuadraticInteger tmp = gunit.modPow(pow, factorial);
		return (QuadraticFieldGeneralForm)tmp.getConjugateNumber();
	}

	private static BigIntegerFraction getUnitPart(PrintStream writer, int unit, int j, boolean debug) throws Exception
	{
		BigIntegerFraction result = getOnePart(rows[unit], j);
		QuadraticFieldGeneralForm gunit = (QuadraticFieldGeneralForm)UNITS[unit].modPow(pows[unit], factorial);
		//writer.println("unit^" + pows[unit] + "(mod|An|) = " + gunit);
		BigInteger numerator = gunit.getA().multiply(TWO).add(gunit.getB()).multiply(BigInteger.valueOf(e[unit]));
		if (debug)
		{
			writer.print(numerator + " * " + result);
		}
		result = result.multiply(new BigIntegerFraction(numerator));
		return result;
	}

	private static BigIntegerFraction getStaticPart(PrintStream writer, int j) throws Exception
	{
		BigIntegerFraction fraction = new BigIntegerFraction(0);
		for (int i = 0;i < partitions1.size();i++)
		{
			Partition p1 = partitions1.get(i);
			Partition p2 = partitions2.get(j);
			Matrix value = constructor.getValue(p1, p2);
			if (value.getRows() > 1)
			{
				continue;
			}
			BigIntegerFraction onePart = getOnePart(i, j);
			writer.print(" " + onePart);
			fraction = fraction.add(onePart);
		}
		writer.println();
		return fraction;
	}

	private static BigIntegerFraction getOnePart(int i, int j) throws Exception
	{
		BigIntegerFraction result;
		Partition p1 = partitions1.get(i);
		Partition p2 = partitions2.get(j);
		Matrix value = constructor.getValue(p1, p2);
		/*
		if (value.getCols() > 1)
		{
			throw new RuntimeException("I can't process special column");
		}*/
		if (!(value.getValue(0, 0) instanceof BigInteger))
		{
			throw new RuntimeException("I can't process not BigIntegers");
		}
		BigInteger tmp = (BigInteger)value.getValue(0, 0);//p1xp2
		tmp = tmp.multiply(AlternatingCharTableUtil.calcAlternatingDegree(p1));//mul deg
		result =new BigIntegerFraction(tmp, factorial);
		return result;
	}

	private static Integer[] getRows()
	{
		List<Integer> rows = new ArrayList<Integer>();
		for (int i = 0; i < partitions1.size(); i++)
		{
			try
			{
				Matrix m = constructor.getValue(partitions1.get(i), partitions2.get(0));
				if (m.getRows() == 2)
				{
					rows.add(i);
				}
			}
			catch (Exception e)
			{
				throw new RuntimeException(e);
			}
		}
		return rows.toArray(new Integer[0]);
	}

}
